// SPDX-License-Identifier: GPL-2.0

/**
 * Copyright (C) 2025 Huawei Technologies Co., Ltd.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, see <https://www.gnu.org/licenses/>.
 *
 * @file platform_info.c
 * @brief Hisilicon platform specific information gathering implementation
 * @details The kernel driver implementation for Hisilicon platform specific information gathering,
 * which is used by Kunpeng Cache Management Framework.
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__

#include "platform_info.h"
#include "hw_regs_access.h"

#include <linux/slab.h>

/**
 * Taishan V100 & LinxiCore common definitions
 */
#define GET_CCIDX(val)							((val >> 20) & GENMASK(3,0))
#define CCSIDR_EL1_ASSOC(val, ccidx)			((ccidx != 0) ? ((val >> 3) & GENMASK(20,0)) : ((val >> 3) & GENMASK(9,0)))

hisi_hw_caps_t hisi_hw_caps;

bool
cman_drv_is_cpu_online(s32 cpu)
{
	if (!cpu_online(cpu)) {
		pr_err("Provided CPU id %d is not online!\n", cpu);
		return false;
	}

	return true;
}

/**
 * Initializes common platform constants.
 *
 * Note: this function is common for Taishan V100 & LinxiCore.
 *
 * @return 0 in case of successfull constants setting.
 *         -EIO   in case of error during write to one of HW registers.
 */
static noinline s32
cman_drv_platform_info_init_common(void)
{
	u64 val = 0;
	u32 ccidx = 0, csselr_val = 2;
	u64 numsets = 0, linesize = 0;

	val = cman_drv_memory_model_feature_reg_read();
	ccidx = GET_CCIDX(val);
	if (!cman_drv_csselr_el1_write(csselr_val))
		return -EIO;
	val = cman_drv_ccsidr_el1_read();
	linesize = (val & GENMASK(2, 0)) + 4;
	linesize = 1 << linesize;
	hisi_hw_caps.l2.cacheline_sz = linesize;
	hisi_hw_caps.l2.cws_num = CCSIDR_EL1_ASSOC(val, ccidx) + 1;
	if(hisi_hw_caps.l2.cws_num >= 64 || hisi_hw_caps.l2.cws_num < 2) {
		pr_err("cws_num is invalid, cws_num = %llu\n", hisi_hw_caps.l2.cws_num);
		return -EINVAL;
	}
	hisi_hw_caps.l2.max_mask = GENMASK(hisi_hw_caps.l2.cws_num - 1, 0);

	if (ccidx != 0) {
		val = cman_drv_ccsidr2_el1_read();
		numsets = (val & GENMASK(23,0)) + 1;
	} else {
		numsets = ((val >> 13) & GENMASK(14,0)) + 1;
	}

	hisi_hw_caps.l2.restrict_granularity = numsets * hisi_hw_caps.l2.cacheline_sz;
	hisi_hw_caps.l2.capacity = hisi_hw_caps.l2.restrict_granularity * hisi_hw_caps.l2.cws_num;

	return 0;
}

/**
 * Initializes platform constants.
 *
 * @return 0 in case of successfull constants setting.
 *         -EIO    in case of error during write to one of HW registers.
 */
s32
cman_drv_platform_info_init(void)
{
	s32 ret = -1;
	ret = cman_drv_platform_info_init_common();

	return ret;
}
